LIBRERÍAS¶

In [ ]:
#instalar mglearn en su ambiente para Machine Learning
#pip install mglearn

import warnings
warnings.filterwarnings("ignore")
import pandas as pd
import mglearn
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
import seaborn as sns
X, y = mglearn.datasets.make_forge()
from sklearn.model_selection import train_test_split
from scipy.stats import kurtosis
from sklearn.linear_model import LinearRegression
from sklearn.linear_model import Ridge
from sklearn.linear_model import Lasso
from sklearn.linear_model import LogisticRegression
from sklearn.svm import LinearSVC
from sklearn.datasets import make_blobs
In [ ]:
#Trazar el conjunto de datos
mglearn.discrete_scatter(X[:, 0], X[:, 1], y)
#loc=4 especifica la ubicación de la leyenda en el gráfico (esquina inferior derecha)
plt.legend(["Class 0", "Class 1"], loc=4)
plt.xlabel("First feature")
plt.ylabel("Second feature")
print("X.shape: {}".format(X.shape))
X.shape: (26, 2)
No description has been provided for this image

Esta línea de código utiliza la función mglearn.discrete_scatter para crear un gráfico de dispersión. Toma los valores de las dos primeras columnas de la matriz X (columna 0 y columna 1) como las coordenadas x e y de los puntos de datos y utiliza los valores de la matriz y para colorear los puntos según sus clases o etiquetas. En este caso, se asume que tienes dos clases (Class 0 y Class 1).Como se puede ver en X.shape, este dataset consta de 26 puntos de datos, con 2 características.

Para ilustrar los algoritmos de regresión, utilizaremos el dataset sintético wave. Este dataset tiene una única característica de entrada y una variable objetivo continua (o respuesta) que queremos modelar.

In [ ]:
X, y = mglearn.datasets.make_wave(n_samples=40)
plt.plot(X, y, '*')
plt.ylim(-3, 3)
plt.xlabel("Feature")
plt.ylabel("Target");
No description has been provided for this image

Complementaremos estos pequeños datasets sintéticos con dos conjuntos de datos del mundo real que se incluyen en scikit-learn. Uno de ellos es el conjunto de datos de cáncer de mama de Wisconsin (cancer, para abreviar), que registra mediciones clínicas de tumores de cáncer de mama. Cada tumor se etiqueta como benign (para tumores inofensivos) o malignant (para tumores cancerosos).

In [ ]:
from sklearn.datasets import load_breast_cancer
cancer = load_breast_cancer()
print("cancer.keys(): \n{}".format(cancer.keys()))
# Esta línea de código imprime las claves (keys) del objeto cancer. En scikit-learn, los conjuntos de datos cargados 
# #suelen ser diccionarios de Python, y esta línea muestra las claves disponibles en ese diccionario.
# #Esto es útil para conocer la estructura de los datos cargados.
cancer.keys(): 
dict_keys(['data', 'target', 'frame', 'target_names', 'DESCR', 'feature_names', 'filename', 'data_module'])
In [ ]:
print("Shape of cancer data: {}".format(cancer.data.shape))
Shape of cancer data: (569, 30)
In [ ]:
cancer.target_names
Out[ ]:
array(['malignant', 'benign'], dtype='<U9')
In [ ]:
print("Conteo de la muestra por clases:\n{}".format(
{n: v for n, v in zip(cancer.target_names, np.bincount(cancer.target))}))
Conteo de la muestra por clases:
{'malignant': 212, 'benign': 357}

{n: v for n, v in zip(cancer.target_names, np.bincount(cancer.target))}: Esta expresión crea un diccionario de Python que muestra la cantidad de muestras por clase en el conjunto de datos. Utiliza la función np.bincount de NumPy para contar las ocurrencias de cada valor en el arreglo cancer.target, que contiene las etiquetas de las muestras. Luego, utiliza zip para combinar estas cuentas con los nombres de las clases, que se encuentran en cancer.target_names. El resultado es un diccionario donde las claves son los nombres de las clases y los valores son las cantidades de muestras de cada clase.

In [ ]:
print("Feature names:\n{}".format(cancer.feature_names))
Feature names:
['mean radius' 'mean texture' 'mean perimeter' 'mean area'
 'mean smoothness' 'mean compactness' 'mean concavity'
 'mean concave points' 'mean symmetry' 'mean fractal dimension'
 'radius error' 'texture error' 'perimeter error' 'area error'
 'smoothness error' 'compactness error' 'concavity error'
 'concave points error' 'symmetry error' 'fractal dimension error'
 'worst radius' 'worst texture' 'worst perimeter' 'worst area'
 'worst smoothness' 'worst compactness' 'worst concavity'
 'worst concave points' 'worst symmetry' 'worst fractal dimension']

VECINOS MÁS CERCANOS: (k-nearest neighbors) (k-NN)¶

Su enfoque consiste en memorizar el conjunto de entrenamiento y luego predecir la etiqueta del vecino más cercano en dicho conjunto. Este método se basa en la idea de que las características utilizadas para describir los puntos en el dominio son relevantes para determinar sus etiquetas, de manera que es probable que puntos cercanos tengan la misma etiqueta

Clasificación -vecinos: En su versión más sencilla, el algoritmo k-NN sólo considera exactamente un vecino más cercano, que es el dato de entrenamiento más cercano al punto para el que queremos hacer una predicción. La predicción es entonces simplemente la salida conocida para este punto de entrenamiento.

In [ ]:
mglearn.plots.plot_knn_classification(n_neighbors=1)
No description has been provided for this image

En lugar de considerar sólo al vecino más cercano, también podemos considerar un número arbitrario , k de vecinos. De ahí viene el nombre del algoritmo k-vecinos más cercanos. Cuando se considera más de un vecino, se utiliza la votación para asignar una etiqueta. Esto significa que, para cada punto de prueba, contamos cuántos vecinos pertenecen a clase 0 y cuántos vecinos pertenecen a la clase 1. A continuación, asignamos la clase que es más frecuente: es decir, la clase mayoritaria entre los k vecinos más cercanos

In [ ]:
mglearn.plots.plot_knn_classification(n_neighbors=4)
No description has been provided for this image

En primer lugar, dividimos nuestros datos en un conjunto de entrenamiento y otro de prueba para poder evaluar el rendimiento de la generalización. random_state=0 nos asegura que obtendremos los mismos conjuntos de training y test para diferentes ejecuciones

In [ ]:
from sklearn.model_selection import train_test_split
#cuando no es suministrado el porcentaje de entrenamiento train_test_split considera este
# #porcentaje para el test como el 25%, esto es test_size=0.25
In [ ]:
cancer.target.shape
Out[ ]:
(569,)

df.insert(0, 'diagnosis', cancer.target): Esta línea de código inserta una nueva columna llamada 'diagnosis' en el DataFrame df en la posición 0 (es decir, al principio del DataFrame). La columna 'diagnosis' se llena con los valores de cancer.target, que son las etiquetas de las muestras y representan el diagnóstico de cáncer (0 para benigno y 1 para maligno).

In [ ]:
df = pd.DataFrame(cancer.data, columns = cancer.feature_names)
df.insert(0, 'diagnosis', cancer.target)
df.head()
Out[ ]:
diagnosis mean radius mean texture mean perimeter mean area mean smoothness mean compactness mean concavity mean concave points mean symmetry ... worst radius worst texture worst perimeter worst area worst smoothness worst compactness worst concavity worst concave points worst symmetry worst fractal dimension
0 0 17.99 10.38 122.80 1001.0 0.11840 0.27760 0.3001 0.14710 0.2419 ... 25.38 17.33 184.60 2019.0 0.1622 0.6656 0.7119 0.2654 0.4601 0.11890
1 0 20.57 17.77 132.90 1326.0 0.08474 0.07864 0.0869 0.07017 0.1812 ... 24.99 23.41 158.80 1956.0 0.1238 0.1866 0.2416 0.1860 0.2750 0.08902
2 0 19.69 21.25 130.00 1203.0 0.10960 0.15990 0.1974 0.12790 0.2069 ... 23.57 25.53 152.50 1709.0 0.1444 0.4245 0.4504 0.2430 0.3613 0.08758
3 0 11.42 20.38 77.58 386.1 0.14250 0.28390 0.2414 0.10520 0.2597 ... 14.91 26.50 98.87 567.7 0.2098 0.8663 0.6869 0.2575 0.6638 0.17300
4 0 20.29 14.34 135.10 1297.0 0.10030 0.13280 0.1980 0.10430 0.1809 ... 22.54 16.67 152.20 1575.0 0.1374 0.2050 0.4000 0.1625 0.2364 0.07678

5 rows × 31 columns

Aquí 0=malignant, 1=benign. Verifiquemos que tipos de datos contiene el dataset. La función info() proporciona información sobre los tipos de datos, columnas, recuento de valores nulos, uso de memoria, etc.

In [ ]:
df.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 569 entries, 0 to 568
Data columns (total 31 columns):
 #   Column                   Non-Null Count  Dtype  
---  ------                   --------------  -----  
 0   diagnosis                569 non-null    int32  
 1   mean radius              569 non-null    float64
 2   mean texture             569 non-null    float64
 3   mean perimeter           569 non-null    float64
 4   mean area                569 non-null    float64
 5   mean smoothness          569 non-null    float64
 6   mean compactness         569 non-null    float64
 7   mean concavity           569 non-null    float64
 8   mean concave points      569 non-null    float64
 9   mean symmetry            569 non-null    float64
 10  mean fractal dimension   569 non-null    float64
 11  radius error             569 non-null    float64
 12  texture error            569 non-null    float64
 13  perimeter error          569 non-null    float64
 14  area error               569 non-null    float64
 15  smoothness error         569 non-null    float64
 16  compactness error        569 non-null    float64
 17  concavity error          569 non-null    float64
 18  concave points error     569 non-null    float64
 19  symmetry error           569 non-null    float64
 20  fractal dimension error  569 non-null    float64
 21  worst radius             569 non-null    float64
 22  worst texture            569 non-null    float64
 23  worst perimeter          569 non-null    float64
 24  worst area               569 non-null    float64
 25  worst smoothness         569 non-null    float64
 26  worst compactness        569 non-null    float64
 27  worst concavity          569 non-null    float64
 28  worst concave points     569 non-null    float64
 29  worst symmetry           569 non-null    float64
 30  worst fractal dimension  569 non-null    float64
dtypes: float64(30), int32(1)
memory usage: 135.7 KB
In [ ]:
df.describe()
Out[ ]:
diagnosis mean radius mean texture mean perimeter mean area mean smoothness mean compactness mean concavity mean concave points mean symmetry ... worst radius worst texture worst perimeter worst area worst smoothness worst compactness worst concavity worst concave points worst symmetry worst fractal dimension
count 569.000000 569.000000 569.000000 569.000000 569.000000 569.000000 569.000000 569.000000 569.000000 569.000000 ... 569.000000 569.000000 569.000000 569.000000 569.000000 569.000000 569.000000 569.000000 569.000000 569.000000
mean 0.627417 14.127292 19.289649 91.969033 654.889104 0.096360 0.104341 0.088799 0.048919 0.181162 ... 16.269190 25.677223 107.261213 880.583128 0.132369 0.254265 0.272188 0.114606 0.290076 0.083946
std 0.483918 3.524049 4.301036 24.298981 351.914129 0.014064 0.052813 0.079720 0.038803 0.027414 ... 4.833242 6.146258 33.602542 569.356993 0.022832 0.157336 0.208624 0.065732 0.061867 0.018061
min 0.000000 6.981000 9.710000 43.790000 143.500000 0.052630 0.019380 0.000000 0.000000 0.106000 ... 7.930000 12.020000 50.410000 185.200000 0.071170 0.027290 0.000000 0.000000 0.156500 0.055040
25% 0.000000 11.700000 16.170000 75.170000 420.300000 0.086370 0.064920 0.029560 0.020310 0.161900 ... 13.010000 21.080000 84.110000 515.300000 0.116600 0.147200 0.114500 0.064930 0.250400 0.071460
50% 1.000000 13.370000 18.840000 86.240000 551.100000 0.095870 0.092630 0.061540 0.033500 0.179200 ... 14.970000 25.410000 97.660000 686.500000 0.131300 0.211900 0.226700 0.099930 0.282200 0.080040
75% 1.000000 15.780000 21.800000 104.100000 782.700000 0.105300 0.130400 0.130700 0.074000 0.195700 ... 18.790000 29.720000 125.400000 1084.000000 0.146000 0.339100 0.382900 0.161400 0.317900 0.092080
max 1.000000 28.110000 39.280000 188.500000 2501.000000 0.163400 0.345400 0.426800 0.201200 0.304000 ... 36.040000 49.540000 251.200000 4254.000000 0.222600 1.058000 1.252000 0.291000 0.663800 0.207500

8 rows × 31 columns

Realicemos tabla de frecuencias y diagrama de barras para nuestra variable respuesta

In [ ]:
df.diagnosis.value_counts()
Out[ ]:
diagnosis
1    357
0    212
Name: count, dtype: int64
In [ ]:
sns.set_style("ticks") #"whitegrid"
plt.title('Tipo de cancer: conteo')
sns.countplot(x=df.diagnosis)
plt.xlabel('Diagnóstico')
plt.ylabel('Frequency')
plt.show()
No description has been provided for this image

otras opciones: "darkgrid": Fondo gris oscuro con una cuadrícula.

"whitegrid": Fondo blanco con una cuadrícula.

"dark": Fondo gris oscuro sin cuadrícula.

"white": Fondo blanco sin cuadrícula.

"ticks": Fondo blanco con marcas de graduación de eje en las cuatro direcciones.

Nótese que nuestro dataset está desbalanceado. Existen técnicas como SMOTE y Stratified sampling, que pueden utilizarse para mejorar el score de clasificación de datos desbalanceados. Verifiquemos además si existen datos faltantes en nuestro Dataframe

In [ ]:
df.isnull().sum()
Out[ ]:
diagnosis                  0
mean radius                0
mean texture               0
mean perimeter             0
mean area                  0
mean smoothness            0
mean compactness           0
mean concavity             0
mean concave points        0
mean symmetry              0
mean fractal dimension     0
radius error               0
texture error              0
perimeter error            0
area error                 0
smoothness error           0
compactness error          0
concavity error            0
concave points error       0
symmetry error             0
fractal dimension error    0
worst radius               0
worst texture              0
worst perimeter            0
worst area                 0
worst smoothness           0
worst compactness          0
worst concavity            0
worst concave points       0
worst symmetry             0
worst fractal dimension    0
dtype: int64

Pasamos a verficar si existe correlación entre las características: df.iloc[:, 1:]: Esta parte del código selecciona todas las filas (:) del DataFrame df y todas las columnas a partir de la segunda columna en adelante. En otras palabras, excluye la primera columna del DataFrame, que suele ser la columna que contiene las etiquetas o nombres de las muestras. corr.shape: Finalmente, se utiliza la propiedad .shape de la matriz de correlación corr para obtener sus dimensiones.

In [ ]:
corr = df.iloc[: , 1:].corr()
corr.shape
Out[ ]:
(30, 30)

Esta parte del código crea una matriz de ceros con las mismas dimensiones que la matriz de correlación corr. En otras palabras, crea una matriz de ceros con el mismo número de filas y columnas que corr. Esto inicializa la matriz de máscara con todos los valores en cero.

mask[np.triu_indices_from(mask)] = True: Luego, se utiliza la función np.triu_indices_from() para obtener los índices de la mitad superior de la matriz (es decir, los índices de la triangular superior). Estos índices corresponden a los elementos que queremos ocultar en la visualización de la matriz de correlación. Finalmente, se establecen los valores en la matriz de máscara mask en True en los índices obtenidos en el paso anterior. Esto significa que todos los elementos en la triangular superior de la matriz de máscara se establecen en True, mientras que los elementos en la mitad inferior se mantienen en False

In [ ]:
mask = np.zeros_like(corr)
mask[np.triu_indices_from(mask)] = True

El código que proporcionaste utiliza la biblioteca Seaborn (sns) y Matplotlib (plt) para visualizar una matriz de correlación en forma de un mapa de calor (heatmap). Aquí hay una explicación de lo que hace este código:

sns.set(font_scale=1.8): Esta línea de código establece el tamaño de fuente para los elementos de la visualización utilizando la función sns.set. font_scale=1.8 aumenta el tamaño de fuente en 1.8 veces el valor predeterminado. Esto se hace para mejorar la legibilidad de los elementos en la visualización. sns.heatmap(corr, mask=mask, cbar=True, fmt='.1f', annot=True, annot_kws={'size': 15}, cmap='Reds'): Esta línea de código crea el mapa de calor utilizando la función sns.heatmap. Los argumentos y opciones utilizados son los siguientes:

corr: La matriz de correlación que se va a visualizar. mask=mask: La matriz de máscara que oculta la triangular superior de la matriz de correlación, como se explicó anteriormente. cbar=True: Muestra la barra de color (barra de colores) a la derecha del mapa de calor para indicar los valores de correlación. fmt='.1f': Formato de cadena para los valores mostrados en el mapa de calor, con una cifra decimal. annot=True: Muestra los valores de correlación en cada celda del mapa de calor. annot_kws={'size': 15}: Opciones para personalizar el tamaño del texto de los valores de correlación. cmap='Reds': El mapa de colores utilizado para representar los valores de correlación. En este caso, se utiliza el esquema de colores 'Reds', que va de tonos más claros a más oscuros.

'viridis': Un mapa de colores perceptualmente uniforme que va de tonos amarillos a verdes y azules.

'plasma': Un mapa de colores con transiciones suaves desde tonos oscuros hasta colores brillantes.

'inferno': Un mapa de colores que va desde el naranja oscuro hasta el amarillo brillante.

'magma': Un mapa de colores que comienza en tonos morados oscuros y termina en colores brillantes.

'cividis': Un mapa de colores diseñado para ser amigable con las personas con deficiencias visuales de color.

'coolwarm': Un mapa de colores que combina colores fríos y cálidos para resaltar tanto valores bajos como altos en el mapa de calor.

'Reds': Un mapa de colores que varía desde tonos claros hasta rojos oscuros.

'Blues': Un mapa de colores que varía desde tonos claros hasta azules oscuros.

In [ ]:
sns.set(font_scale=1.8)
plt.figure(figsize=(20,20))
sns.heatmap(corr, mask=mask, cbar=True, fmt='.1f', annot=True, annot_kws={'size':15}, cmap='Blues');
No description has been provided for this image

Trazamos histogramas de cada característica en nuestro dataset

In [ ]:
sns.set(font_scale=1.8)
df.iloc[: , 1:].hist(figsize = (30,30), color = 'Blue');
No description has been provided for this image
In [ ]:
sns.set(font_scale=1.8)
plt.figure(figsize=(20,20))
sns.boxplot(data= df.drop(['diagnosis'], axis=1) ,width=0.5 , saturation=0.9, orient="h");
No description has been provided for this image

melted_data = pd.melt(df, id_vars="diagnosis", value_vars=['worst radius', 'worst texture', 'worst perimeter']): Esta línea de código utiliza la función pd.melt para transformar el DataFrame df. La función melt toma varias columnas y las "derrite" en una sola columna, mientras que mantiene una columna identificadora ('diagnosis' en este caso) como referencia. En este caso, las columnas 'worst radius,' 'worst texture,' y 'worst perimeter' se derriten en una sola columna llamada "value" y se utiliza "diagnosis" como identificador.

sns.boxplot(x="value", y="variable", hue="diagnosis", data=melted_data): Esta línea de código crea el gráfico de caja utilizando la función sns.boxplot. Aquí están los argumentos y opciones utilizados:

x="value": Los valores que se distribuirán en el gráfico de caja, que son las características derritidas en la columna "value."

y="variable": Las variables o características originales que se muestran en el eje vertical del gráfico de caja.

hue="diagnosis": Divide los datos por diagnóstico (benigno o maligno), lo que significa que se crearán dos cajas por cada característica, una para cada diagnóstico.

data=melted_data: Los datos que se utilizarán para crear el gráfico de caja, que son los datos derretidos.

In [ ]:
melted_data = pd.melt(df, id_vars = "diagnosis",value_vars = ['mean radius', 'mean texture','mean perimeter'])
sns.set(font_scale=1.8)
plt.figure(figsize = (20,10))
sns.boxplot(x = "value", y = "variable", hue="diagnosis",data= melted_data);
No description has been provided for this image
In [ ]:
melted_data = pd.melt(df, id_vars = "diagnosis",value_vars = ['radius error', 
                                                              'texture error', 'perimeter error'])
sns.set(font_scale=1.8)
plt.figure(figsize = (20,10))
sns.boxplot(x = "value", y = "variable", hue="diagnosis",data= melted_data);
No description has been provided for this image
In [ ]:
melted_data = pd.melt(df, id_vars = "diagnosis",value_vars = ['worst radius', 
                                                              'worst texture', 'worst perimeter'])
sns.set(font_scale=1.8)
plt.figure(figsize = (20,10))
sns.boxplot(x = "value", y = "variable", hue="diagnosis",data= melted_data);
No description has been provided for this image

El código que proporcionaste utiliza Seaborn para crear una matriz de gráficos de dispersión (scatter plots) junto con histogramas en la diagonal para visualizar relaciones entre múltiples características del conjunto de datos. Estos gráficos se organizan en una matriz de pares, donde cada par de características se compara en función del diagnóstico ("M" para maligno y "B" para benigno). Aquí tienes una explicación paso a paso:

sns.pairplot(data=df[columns], hue="diagnosis", palette='viridis', corner=True, height=7.0, aspect=1.0): Esta línea de código crea la matriz de gráficos de dispersión utilizando la función sns.pairplot. Aquí están los argumentos y opciones utilizados:

data=df[columns]: Especifica los datos que se utilizarán en la matriz de gráficos de dispersión. Se seleccionan solo las columnas mencionadas en la lista columns.

hue="diagnosis": Divide los datos por diagnóstico (benigno o maligno), lo que significa que se mostrarán diferentes colores en función del diagnóstico.

palette='viridis': Establece el esquema de colores que se utilizará en los gráficos. En este caso, se utiliza el esquema de colores 'viridis'.

corner=True: Esta opción coloca histogramas en la diagonal principal de la matriz de gráficos de dispersión para mostrar la distribución de cada característica.

height=7.0: Establece la altura de los gráficos en la matriz.

aspect=1.0: Establece la relación de aspecto de los gráficos. En este caso, se mantiene la relación de aspecto predeterminada.

In [ ]:
columns = ['diagnosis', 'mean radius', 'mean texture', 'mean perimeter', 'mean area', 'mean smoothness', 'mean compactness', 'mean concavity']
sns.set(font_scale=4.0)
sns.pairplot(data=df[columns], hue="diagnosis", palette='viridis', corner=True, height=7.0, aspect=1.0);
No description has been provided for this image

Diagrama de densidad de distribución KDE y distribución mediante el diagrama de dispersión stripplot()

El código que proporcionaste configura algunas opciones de estilo para Seaborn y luego crea una gráfica de densidad de kernel (kernel density estimate, KDE) utilizando FacetGrid. Aquí tienes una explicación paso a paso:

matplotlib.rc_file_defaults(): Esta línea de código restablece la configuración de estilo de Matplotlib a los valores predeterminados. Esto puede ser útil si deseas eliminar cualquier configuración personalizada previa que haya sido aplicada.

sns.set_style("ticks"): Establece el estilo de fondo y los ejes de Seaborn en "ticks". Esto significa que los ejes tendrán marcas de graduación en forma de "ticks" en lugar del estilo predeterminado "whitegrid" que incluye una cuadrícula.

sns.FacetGrid(df, hue="diagnosis", height=6): Aquí se crea un objeto FacetGrid de Seaborn. FacetGrid se utiliza para crear múltiples gráficos basados en una variable categórica (hue en este caso). El argumento hue="diagnosis" significa que se crearán gráficos para cada valor único en la columna "diagnosis" (que es "M" para maligno y "B" para benigno) y se diferenciarán por color.

.map(sns.kdeplot, "mean radius"): Se utiliza el método .map para mapear una gráfica de densidad de kernel (KDE) a los datos. El argumento "mean radius" especifica la columna de datos que se utilizará para el KDE.

.add_legend(): Esta función agrega una leyenda al gráfico para indicar qué color corresponde a cada valor único en la columna "diagnosis" (en este caso, "M" y "B")

In [ ]:
matplotlib.rc_file_defaults()
sns.set_style("ticks")
sns.FacetGrid(df, hue="diagnosis", height=6).map(sns.kdeplot, "mean radius").add_legend();
No description has been provided for this image

sns.stripplot(x="diagnosis", y="mean radius", data=df, jitter=True, edgecolor="gray"): Esta línea de código crea el gráfico de dispersión utilizando la función sns.stripplot. Aquí están los argumentos y opciones utilizados:

x="diagnosis": Especifica que los valores de diagnóstico se mostrarán en el eje x (horizontal).

y="mean radius": Especifica que los valores de "mean radius" se mostrarán en el eje y (vertical).

data=df: Los datos que se utilizarán para crear el gráfico, que provienen del DataFrame df.

jitter=True: El parámetro jitter se utiliza para agregar un pequeño desplazamiento aleatorio a los puntos a lo largo del eje x. Esto se hace para evitar la superposición de puntos que tienen el mismo valor en el eje x y hacer que los puntos sean más visibles en el gráfico.

edgecolor="gray": Establece el color del borde de los puntos en el gráfico.

In [ ]:
sns.stripplot(x="diagnosis", y="mean radius", data=df, jitter=True, edgecolor="gray");
No description has been provided for this image

La multicolinealidad es un problema ya que reduce la importancia de las variables independientes. A fin de solucionar este problem, se eliminan los predictores altamente correlacionados. Podemos comprobar la presencia de multicolinealidad entre algunas de las variables. Por ejemplo, la columna mean radius tiene una correlación de 1.0 con las columnas mean perimeter y mean area, respectivamente. Esto se debe a que las tres columnas contienen esencialmente la misma información, que es el tamaño físico de la observación.

Por lo tanto, sólo debemos elegir una de las tres columnas cuando pasemos al análisis posterior. Otro lugar donde la multicolinealidad es evidente, es entre las columnas "mean" y "worst". Por ejemplo, la columna mean radius tiene una correlación de 1.0 con la columna worst radius. También hay multicolinealidad entre los atributos compactness, concavity y concave points. Así que podemos elegir sólo uno de estos, por ejemplo compactness. De la matriz de correlación sabemos que estas columnas están altamente correlacionadas con las columnas mean radius, perimeter, area. Por lo tanto, estas columnas serán eliminadas.

inplace=True: Este argumento indica que la eliminación se realizará directamente en el DataFrame df en lugar de crear una copia modificada. Cuando inplace=True, el DataFrame se modifica en su lugar y no se devuelve un nuevo DataFrame.

In [ ]:
cols = ['worst radius', 
        'worst texture', 
        'worst perimeter', 
        'worst area', 
        'worst smoothness', 
        'worst compactness', 
        'worst concavity',
        'worst concave points', 
        'worst symmetry', 
        'worst fractal dimension']
df.drop(cols, inplace=True, axis=1);

cols = ['mean perimeter',
        'mean area',
        'perimeter error', 
        'area error']
df.drop(cols, inplace=True, axis=1);

cols = ['mean concavity',
        'concavity error', 
        'mean concave points', 
        'concave points error']
df.drop(cols, inplace=True, axis=1);
In [ ]:
df.columns
Out[ ]:
Index(['diagnosis', 'mean radius', 'mean texture', 'mean smoothness',
       'mean compactness', 'mean symmetry', 'mean fractal dimension',
       'radius error', 'texture error', 'smoothness error',
       'compactness error', 'symmetry error', 'fractal dimension error'],
      dtype='object')

corr = df.corr().round(2): Esta línea de código calcula la matriz de correlación entre las características del DataFrame df y redondea los valores a dos decimales. La matriz de correlación muestra cómo están relacionadas linealmente las características entre sí.

cmap = sns.diverging_palette(220, 10, as_cmap=True): Se crea un mapa de colores personalizado (cmap) utilizando la función sns.diverging_palette. Este mapa de colores va desde un color (azul en este caso) a otro color (naranja) y es útil para visualizar relaciones simétricas, ya que se considera un mapa de colores divergente.

mask = np.zeros_like(corr): Se crea una matriz de máscara (mask) de ceros con las mismas dimensiones que la matriz de correlación corr. Esta matriz se utilizará para ocultar la mitad superior de la matriz de correlación en el gráfico de calor.

mask[np.triu_indices_from(mask)] = True: Se establecen los valores en True en la matriz de máscara mask en la triangular superior de la matriz. Esto oculta la mitad superior de la matriz de correlación, ya que no contiene información nueva y evitará la redundancia en el gráfico de calor.

f, ax = plt.subplots(figsize=(20, 20)): Se crea una figura de Matplotlib con un tamaño de 20x20 pulgadas para contener el gráfico de calor.

sns.heatmap(corr, mask=mask, cmap=cmap, vmin=-1, vmax=1, center=0, square=True, linewidths=.5, cbar_kws={"shrink": .5}, annot=True): Se utiliza la función sns.heatmap para crear el mapa de calor. Aquí están los argumentos y opciones utilizados:

corr: La matriz de correlación que se va a visualizar. mask=mask: La matriz de máscara que oculta la triangular superior de la matriz de correlación. cmap=cmap: El mapa de colores personalizado creado anteriormente. vmin=-1 y vmax=1: Establecen los valores mínimo y máximo en el mapa de calor para que varíen de -1 a 1, ya que la correlación puede variar en ese rango. center=0: Establece el valor central en 0 en el mapa de calor. square=True: Hace que el mapa de calor tenga una relación de aspecto cuadrada. linewidths=.5: Establece el ancho de las líneas que separan las celdas en el mapa de calor. cbar_kws={"shrink": .5}: Personaliza la barra de color (barra de colores) en el gráfico de calor. annot=True: Muestra los valores de correlación en cada celda del mapa de calor. plt.tight_layout(): Ajusta automáticamente el diseño de la figura para que los elementos se ajusten correctamente.

In [ ]:
corr = df.corr().round(2)

cmap = sns.diverging_palette(220, 10, as_cmap=True)

mask = np.zeros_like(corr)
mask[np.triu_indices_from(mask)] = True

sns.set(font_scale=1.8)
f, ax = plt.subplots(figsize=(20, 20))
sns.heatmap(corr, mask=mask, cmap=cmap, vmin=-1, vmax=1, center=0, square=True, linewidths=.5,
            cbar_kws={"shrink": .5}, annot=True)
plt.tight_layout()
No description has been provided for this image

División en entrenamiento (train) y prueba (test). Dividimos la variable objetivo y las variables independientes. A continuación, evaluamos el rendimiento del conjunto de entrenamiento y de prueba con diferentes números de vecinos

In [ ]:
X = df.drop(['diagnosis'], axis = 1)
y = df['diagnosis']
from sklearn.neighbors import KNeighborsClassifier

X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=cancer.target, random_state=66): Esta línea de código divide los datos en conjuntos de entrenamiento y prueba utilizando la función train_test_split de Scikit-Learn. Aquí están los argumentos utilizados:

X: Representa las características (variables independientes) de tus datos. y: Representa las etiquetas o valores objetivo (variable dependiente) que deseas predecir. stratify=cancer.target: Garantiza que las divisiones de entrenamiento y prueba mantengan la misma proporción de clases que la variable objetivo cancer.target. Esto es importante para problemas de clasificación, especialmente si hay desequilibrios en las clases. random_state=66: Establece una semilla aleatoria para garantizar que la división de los datos sea reproducible. training_accuracy = [] y test_accuracy = []: Estas listas vacías se utilizan para almacenar las puntuaciones de precisión (accuracy) en el conjunto de entrenamiento y prueba para diferentes valores de n_neighbors.

neighbors_settings = range(1, 15): Esta línea de código crea una secuencia de valores n_neighbors desde 1 hasta 14 (inclusive). Estos valores se utilizarán para ajustar el hiperparámetro n_neighbors en el clasificador k-NN.

Bucle for n_neighbors in neighbors_settings:: Este bucle itera a través de los diferentes valores de n_neighbors que se han definido previamente.

clf = KNeighborsClassifier(n_neighbors=n_neighbors): Se crea un clasificador k-NN con el valor actual de n_neighbors. Cada iteración del bucle utiliza un valor diferente de n_neighbors.

clf.fit(X_train, y_train): Se ajusta el modelo k-NN al conjunto de entrenamiento.

training_accuracy.append(clf.score(X_train, y_train)): Se calcula y se almacena la precisión del modelo en el conjunto de entrenamiento utilizando clf.score(), y el resultado se agrega a la lista training_accuracy.

test_accuracy.append(clf.score(X_test, y_test)): Se calcula y se almacena la precisión del modelo en el conjunto de prueba utilizando clf.score(), y el resultado se agrega a la lista test_accuracy

In [ ]:
X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=cancer.target, random_state=66)

training_accuracy = []
test_accuracy = []

neighbors_settings = range(1, 15)
for n_neighbors in neighbors_settings:
    clf = KNeighborsClassifier(n_neighbors=n_neighbors)
    clf.fit(X_train, y_train)
    training_accuracy.append(clf.score(X_train, y_train))
    test_accuracy.append(clf.score(X_test, y_test))

Cuando llamas a matplotlib.rc_file_defaults(), estás deshaciendo cualquier configuración personalizada que hayas realizado anteriormente en tu sesión de Python y devolviendo Matplotlib a su estado inicial con los valores predeterminados. Esto puede ser útil si has realizado cambios en la configuración de Matplotlib y deseas volver a la configuración original.

In [ ]:
matplotlib.rc_file_defaults()
plt.plot(neighbors_settings, training_accuracy, label="Training accuracy")
plt.plot(neighbors_settings, test_accuracy, label="Test accuracy")
plt.ylabel("Accuracy")
plt.xlabel("n_neighbors")
plt.legend();
No description has been provided for this image

El gráfico muestra accuracy para los conjuntos de entrenamiento y de prueba en el eje contra el ajuste de n_vecinos en el eje . Aunque los gráficos del mundo real no suelen ser muy suaves, podemos reconocer algunas de las características del sobreajuste (overfitting) y del subajuste (underfitting). Si se considera un solo vecino más cercano, la predicción en el conjunto de entrenamiento es perfecta. Pero cuando se consideran más vecinos, el modelo se simplifica y la precisión del entrenamiento disminuye.

La precisión del conjunto de prueba cuando se utiliza un solo vecino es menor que cuando se utilizan más vecinos, lo que indica que el uso de un solo vecino más cercano conduce a una mayor precisión en el conjunto de entrenamiento (modelo demasiado complejo). Pero cuando se consideran más vecinos, el modelo se simplifica y la precisión del entrenamiento disminuye.

In [ ]:
clf = KNeighborsClassifier(n_neighbors=9)
clf.fit(X_train, y_train)

print(clf.score(X_train, y_train), clf.score(X_test, y_test))
0.8990610328638498 0.9020979020979021

Regresión por k-vecinos¶

Cuando se utilizan varios vecinos más cercanos, la predicción es el promedio, o la media, de los vecinos

El algoritmo de k-vecinos más cercanos para la regresión se implementa en la clase KNeighbors Regressor en scikit-learn. Se utiliza de forma similar a KNeighborsClassifier

In [ ]:
mglearn.plots.plot_knn_regression(n_neighbors=3)
No description has been provided for this image
In [ ]:
mglearn.plots.plot_knn_regression(n_neighbors=1)
No description has been provided for this image
In [ ]:
from sklearn.neighbors import KNeighborsRegressor
X, y = mglearn.datasets.make_wave(n_samples=40)
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)
reg = KNeighborsRegressor(n_neighbors=3)
reg.fit(X_train, y_train)
Out[ ]:
KNeighborsRegressor(n_neighbors=3)
In a Jupyter environment, please rerun this cell to show the HTML representation or trust the notebook.
On GitHub, the HTML representation is unable to render, please try loading this page with nbviewer.org.
KNeighborsRegressor(n_neighbors=3)

Ahora podemos hacer predicciones sobre el conjunto de prueba:

In [ ]:
print("Test set predictions:\n{}".format(reg.predict(X_test)))
Test set predictions:
[-0.05396539  0.35686046  1.13671923 -1.89415682 -1.13881398 -1.63113382
  0.35686046  0.91241374 -0.44680446 -1.13881398]

También podemos evaluar el modelo utilizando el método score, que para los regresores devuelve la puntuación R^2. La puntuación R^2 , también conocida como coeficiente de determinación, es una medida de predicción de un modelo de regresión, y arroja una puntuación entre 0 y 1. Un valor de 1 corresponde a una predicción perfecta, y un valor de 0 corresponde a un modelo constante que sólo predice la media de las respuestas del conjunto de entrenamiento, y_train. Aquí, el score es de 0.83, lo que indica un ajuste del modelo relativamente bueno

In [ ]:
print("Test set R^2: {:.2f}".format(reg.score(X_test, y_test)))
Test set R^2: 0.83

Análisis de KNeighborsRegressor¶

In [ ]:
fig, axes = plt.subplots(1, 3, figsize=(15, 4))

line = np.linspace(-3, 3, 1000).reshape(-1, 1)
for n_neighbors, ax in zip([1, 3, 9], axes):
    reg = KNeighborsRegressor(n_neighbors=n_neighbors)
    reg.fit(X_train, y_train)
    ax.plot(line, reg.predict(line))
    ax.plot(X_train, y_train, '^', c=mglearn.cm2(0), markersize=8)
    ax.plot(X_test, y_test, 'v', c=mglearn.cm2(1), markersize=8)
    ax.set_title("{} neighbor(s)\n train score: {:.2f} test score: {:.2f}".format(n_neighbors, 
                                                                                  reg.score(X_train, y_train),
                                                                                  reg.score(X_test, y_test)))
    ax.set_xlabel("Feature")
    ax.set_ylabel("Target")
axes[0].legend(["Model predictions", "Training data/target", "Test data/target"], loc="best");
No description has been provided for this image

Tener en cuenta más vecinos conduce a predicciones más suaves, pero éstas no se ajustan tan bien a los datos de entrenamiento

Aplicación: World Hydropower Generation¶

El conjunto de datos utilizado es una recopilación de la generación de energía de varios países europeos, medida en THh entre 2000 y 2019. Su contenido fue extraído de World in Data

In [ ]:
from sklearn.preprocessing import MinMaxScaler
from sklearn.model_selection import train_test_split
from sklearn.model_selection import GridSearchCV
from sklearn import neighbors
from sklearn.metrics import mean_absolute_error
from sklearn.metrics import mean_squared_error
from sklearn.metrics import mean_squared_log_error
from sklearn.metrics import r2_score
from sklearn.metrics import explained_variance_score

Para medir la eficacia de nuestros modelos generados, utilizamoslas siguientes métricas:

mean_absolute_error (MAE): medida de los errores entre observaciones emparejadas que expresan el mismo fenómeno;

mean_squared_error (root - RMSE): la desviación estándar de los residuos (errores de predicción);

mean_squared_log_error (root - RMSLE): mide la relación entre lo real y lo predicho;

r2_score (R2):: coeficiente de determinación, la proporción de la varianza en la variable dependiente que es predecible a partir de la(s) variable(s) independiente(s); y

explained_variance_score (EVS): mide la discrepancia entre un modelo y los datos reales.

In [ ]:
pd.options.display.float_format = '{:.4f}'.format

scaler = MinMaxScaler(feature_range=(0, 1))

df_power = pd.read_csv('https://raw.githubusercontent.com/lihkir/Data/main/Hydropower_Consumption.csv', sep=',')
In [ ]:
df_power.info()
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 153 entries, 0 to 152
Data columns (total 21 columns):
 #   Column   Non-Null Count  Dtype 
---  ------   --------------  ----- 
 0   Country  153 non-null    object
 1   2000     153 non-null    int64 
 2   2001     153 non-null    int64 
 3   2002     153 non-null    int64 
 4   2003     153 non-null    int64 
 5   2004     153 non-null    int64 
 6   2005     153 non-null    int64 
 7   2006     153 non-null    int64 
 8   2007     153 non-null    int64 
 9   2008     153 non-null    int64 
 10  2009     153 non-null    int64 
 11  2010     153 non-null    int64 
 12  2011     153 non-null    int64 
 13  2012     153 non-null    int64 
 14  2013     153 non-null    int64 
 15  2014     153 non-null    int64 
 16  2015     153 non-null    int64 
 17  2016     153 non-null    int64 
 18  2017     153 non-null    int64 
 19  2018     153 non-null    int64 
 20  2019     153 non-null    int64 
dtypes: int64(20), object(1)
memory usage: 25.2+ KB
In [ ]:
df_power.head()
Out[ ]:
Country 2000 2001 2002 2003 2004 2005 2006 2007 2008 ... 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019
0 Afghanistan 312 498 555 63 565 59 637 748 542 ... 751 595 71 804 895 989 1025 105 105 107
1 Africa 75246 80864 85181 82873 87405 89066 92241 95341 97157 ... 107427 110445 110952 117673 123727 115801 123816 130388 132735 0
2 Albania 4548 3519 3477 5117 5411 5319 4951 276 3759 ... 7673 4036 4725 6959 4726 5866 7136 448 448 4018
3 Algeria 54 69 57 265 251 555 218 226 283 ... 173 378 389 99 193 145 72 56 117 152
4 Angola 903 1007 1132 1229 1733 2197 2638 2472 3103 ... 3666 3967 3734 4719 4991 5037 5757 7576 7576 8422

5 rows × 21 columns

In [ ]:
countries = df_power.Country
In [ ]:
df_power = df_power.drop(columns = ["Country"])
df_power = pd.DataFrame(scaler.fit_transform(df_power), 
                        columns=['2000','2001','2002','2003','2004','2005',
                                 '2006','2007','2008','2009','2010','2011',
                                 '2012','2013','2014','2015','2016','2017',
                                 '2018','2019'])
In [ ]:
df_power.describe()
Out[ ]:
2000 2001 2002 2003 2004 2005 2006 2007 2008 2009 2010 2011 2012 2013 2014 2015 2016 2017 2018 2019
count 153.0000 153.0000 153.0000 153.0000 153.0000 153.0000 153.0000 153.0000 153.0000 153.0000 153.0000 153.0000 153.0000 153.0000 153.0000 153.0000 153.0000 153.0000 153.0000 153.0000
mean 0.0327 0.0353 0.0355 0.0328 0.0419 0.0390 0.0415 0.0434 0.0371 0.0445 0.0328 0.0433 0.0352 0.0290 0.0282 0.0265 0.0306 0.0296 0.0388 0.0259
std 0.1229 0.1262 0.1278 0.1249 0.1372 0.1364 0.1366 0.1440 0.1293 0.1493 0.1208 0.1459 0.1294 0.1121 0.1062 0.1031 0.1117 0.1110 0.1317 0.1043
min 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000 0.0000
25% 0.0002 0.0003 0.0003 0.0003 0.0003 0.0004 0.0003 0.0004 0.0004 0.0003 0.0004 0.0002 0.0004 0.0002 0.0003 0.0002 0.0002 0.0002 0.0003 0.0002
50% 0.0027 0.0030 0.0026 0.0027 0.0037 0.0028 0.0031 0.0039 0.0032 0.0035 0.0043 0.0040 0.0034 0.0025 0.0024 0.0019 0.0025 0.0020 0.0029 0.0019
75% 0.0116 0.0128 0.0119 0.0118 0.0155 0.0107 0.0149 0.0151 0.0147 0.0152 0.0145 0.0156 0.0140 0.0096 0.0121 0.0086 0.0117 0.0113 0.0171 0.0097
max 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000 1.0000

Ahora, el objetivo es crear un modelo que prediga la generación de energía para 2019, basándose en los 18 años anteriores (2000 - 2018) con al menos un 75% de precisión. Para ello, el conjunto de datos se separó en X e y, siendo X datos de predicción e y lo que se pretende predecir. Para ello, se dividen en entrenamiento (70%) y prueba (30%).

In [ ]:
X = df_power.drop(columns = ["2019"])
y = df_power["2019"]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=0)

Como ya tenemos seleccionados los datos de entrenamiento y de prueba, es necesario encontrar el factor k que genere los mejores resultados para el algoritmo. Una de las formas de encontrar este factor k es realizar una prueba con varios valores y medir los resultados porcentuales. Será necesario agotar un gran número de posibilidades de k

In [ ]:
rmsle_val = []
best_rmsle = 1.0

for k in range(20):
    k = k+1
    knn = neighbors.KNeighborsRegressor(n_neighbors = k)

    knn.fit(X_train, y_train) 
    y_pred = knn.predict(X_test)
    rmsle = np.sqrt(mean_squared_log_error(y_test,y_pred))
    if (rmsle < best_rmsle):
        best_rmsle = rmsle
        best_k = k
    rmsle_val.append(rmsle)
    print('RMSLE value for k= ' , k , 'is:', rmsle)

print(f"Best RMSLE: {best_rmsle}, Best k: {best_k}")
RMSLE value for k=  1 is: 0.05771085849033799
RMSLE value for k=  2 is: 0.046287824938614094
RMSLE value for k=  3 is: 0.05245290672301628
RMSLE value for k=  4 is: 0.03904370874530979
RMSLE value for k=  5 is: 0.04413545109970342
RMSLE value for k=  6 is: 0.049397885066996064
RMSLE value for k=  7 is: 0.05368842644105438
RMSLE value for k=  8 is: 0.057205532019402504
RMSLE value for k=  9 is: 0.05970971610365409
RMSLE value for k=  10 is: 0.06264220706736034
RMSLE value for k=  11 is: 0.06519555508343818
RMSLE value for k=  12 is: 0.06683376633168203
RMSLE value for k=  13 is: 0.06835697276199447
RMSLE value for k=  14 is: 0.0700987590270357
RMSLE value for k=  15 is: 0.0714815908074978
RMSLE value for k=  16 is: 0.07276108145829546
RMSLE value for k=  17 is: 0.07381745094856672
RMSLE value for k=  18 is: 0.07488933459172763
RMSLE value for k=  19 is: 0.07579673979403485
RMSLE value for k=  20 is: 0.07667076282857414
Best RMSLE: 0.03904370874530979, Best k: 4

Nuestra métrica indica que el menor error se produce cuando tenemos k=4 (RMSLE de 0.0390), lo que indica un error relativo entre los valores predichos y los actuales del 3,90%. Por lo tanto, presentaremos todos los valores en un gráfico, que nos mostrará visualmente los resultados obtenidos. Esta función se conoce como "función codo", dada la variación porcentual que se produce entre los valores de k, primero hacia abajo y luego hacia arriba, cuando k encuentra su mejor valor. Estamos trazando los valores RMSLE frente a los valores de k .

In [ ]:
curve = pd.DataFrame(rmsle_val)
curve.plot(figsize=(8,5));
No description has been provided for this image

También haremos uso de la función de score, que nos permitirá ver la tasa de precisión de nuestro modelo (80,16%).

In [ ]:
knn = neighbors.KNeighborsRegressor(n_neighbors = 4)

knn.fit(X_train,y_train)
y_pred = knn.predict(X_test)
knn.score(X_test, y_test)
Out[ ]:
0.8016285342188538

Una vez se ha construido el modelo y se prueban algunas predicciones, podemos aplicar las métricas y analizar los resultados. Aquí se comparan el conjunto de prueba con las predicciones usando las métricas R2, EVS, MAE, RMSE, RMSLE, para verificar cuáles son los resultados de cada una de ellas.

In [ ]:
r2_valid = r2_score(y_test, y_pred)
mae_valid = mean_absolute_error(y_test, y_pred)
evs_valid = explained_variance_score(y_test, y_pred, multioutput='uniform_average')
rmse_valid = np.sqrt(mean_squared_error(y_test, y_pred))
rmsle_valid = np.sqrt(mean_squared_log_error(y_test, y_pred))

print('R2 Valid:',r2_valid)
print('EVS Valid:', evs_valid)
print('MAE Valid:', mae_valid)
print('RMSE Valid:',rmse_valid)
print('RMSLE Valid:', rmsle_valid)
R2 Valid: 0.8016285342188538
EVS Valid: 0.8131526313306373
MAE Valid: 0.017960057840249434
RMSE Valid: 0.04999229884993785
RMSLE Valid: 0.03904370874530979

Una vez realizada la predicción y comprobado el modelo, organizamos los resultados uno al lado del otro para poder hacer una comparación.

In [ ]:
country_test = countries[len(countries)-len(y_test):]
In [ ]:
data_prediction = list(zip(y_test,y_pred))
data_prediction = pd.DataFrame(data_prediction, columns=['Test','Prediction'])
data_prediction = data_prediction.set_index(country_test)
data_prediction.head(10)
Out[ ]:
Test Prediction
Country
Papua New Guinea 0.0406 0.0268
Paraguay 0.0044 0.0039
Peru 0.0134 0.0096
Phillipines 0.0280 0.0128
Poland 0.0164 0.0125
Portugal 0.0321 0.0155
Puerto Rico 0.4982 0.3417
Reunion 0.5331 0.3417
Romania 0.0007 0.0075
Russia 0.0032 0.0039

plt.xticks(rotation=90): Establece la rotación de las etiquetas en el eje x a 90 grados para que los nombres de los países sean legibles en el gráfico, especialmente si son largos.

plt.legend(loc="upper right"): Agrega una leyenda al gráfico en la esquina superior derecha para distinguir las líneas "Test" y "Prediction".

In [ ]:
plt.rcParams.update({'font.size': 18});
plt.figure(figsize=(20, 8));
plt.plot(data_prediction.index, data_prediction.Test, label="Test");
plt.plot(data_prediction.index, data_prediction.Prediction, label="Prediction");
plt.xticks(rotation=90);
plt.legend(loc="upper right");
plt.xlabel("Country");
plt.ylabel("Hydropower Generation");
No description has been provided for this image

Modelos lineales¶

Los modelos lineales hacen una predicción utilizando una función lineal de las características de entrada

EDA Análisis exploratorio de datos¶

El EDA puede utilizarse para buscar valores atípicos, patrones y tendencias en los datos.

EDA ayuda a encontrar patrones significativos en los datos.

EDA proporciona una visión en profundidad de los conjuntos de datos para resolver nuestros problemas de negocio.

EDA proporciona una pista para imputar los valores que faltan en el conjunto de datos.

Asimetría

skew = 0: Distribución simétrica (valores aceptables skew $\in$ (-1,1)).

skew > 0: Mayor peso en la cola izquierda de la distribución (sesgo positivo).

skew < 0: Mayor peso en la cola derecha de la distribución (sesgo negativo).

Kurtosis: Determina si una distribución tiene colas gruesas con respecto a la distribución normal. Proporciona información sobre la forma de una distribución de frecuencias.

kurtosis=3: se denomina mesocúrtica (distribución normal).

kurtosis<3: se denomina platicúrtica (distribución con colas menos gruesas que la normal).

kurtosis>3: se denomina leptocúrtica (distribución con colas más gruesas que la normal) y significa que trata de producir más valores atípicos que la distribución normal.

In [ ]:
data = pd.read_csv('https://raw.githubusercontent.com/lihkir/Data/main/boston.csv', index_col=0)
In [ ]:
data.head()
Out[ ]:
crim zn indus chas nox rm age dis rad tax ptratio black lstat medv
1 0.0063 18.0000 2.3100 0 0.5380 6.5750 65.2000 4.0900 1 296 15.3000 396.9000 4.9800 24.0000
2 0.0273 0.0000 7.0700 0 0.4690 6.4210 78.9000 4.9671 2 242 17.8000 396.9000 9.1400 21.6000
3 0.0273 0.0000 7.0700 0 0.4690 7.1850 61.1000 4.9671 2 242 17.8000 392.8300 4.0300 34.7000
4 0.0324 0.0000 2.1800 0 0.4580 6.9980 45.8000 6.0622 3 222 18.7000 394.6300 2.9400 33.4000
5 0.0691 0.0000 2.1800 0 0.4580 7.1470 54.2000 6.0622 3 222 18.7000 396.9000 5.3300 36.2000
In [ ]:
data.info()
<class 'pandas.core.frame.DataFrame'>
Index: 506 entries, 1 to 506
Data columns (total 14 columns):
 #   Column   Non-Null Count  Dtype  
---  ------   --------------  -----  
 0   crim     506 non-null    float64
 1   zn       506 non-null    float64
 2   indus    506 non-null    float64
 3   chas     506 non-null    int64  
 4   nox      506 non-null    float64
 5   rm       506 non-null    float64
 6   age      506 non-null    float64
 7   dis      506 non-null    float64
 8   rad      506 non-null    int64  
 9   tax      506 non-null    int64  
 10  ptratio  506 non-null    float64
 11  black    506 non-null    float64
 12  lstat    506 non-null    float64
 13  medv     506 non-null    float64
dtypes: float64(11), int64(3)
memory usage: 59.3 KB

Información de atributos (por orden):

CRIM: tasa de criminalidad per cápita por ciudad

ZN: proporción de suelo residencial para parcelas de más de 25.000 pies cuadrados

INDUS: proporción de acres comerciales no minoristas por ciudad

CHAS: Variable dummy del Río Charles (= 1 si el tramo limita con el río; 0 en caso contrario)

NOX: concentración de óxidos nítricos (partes por 10 millones)

RM: número medio de habitaciones por vivienda

AGE: proporción de unidades ocupadas por sus propietarios construidas antes de 1940

DIS: distancias ponderadas a cinco centros de empleo de Boston

RAD: índice de accesibilidad a autopistas radiales

TAX: tipo del impuesto sobre bienes inmuebles de valor íntegro por 10.000 dólares

PTRATIO: relación alumnos-profesor por ciudad

B: 1000(Bk - 0,63)^2 donde Bk es la proporción de negros por ciudad

LSTAT: % más bajo de la población

MEDV: Valor medio de las viviendas ocupadas por sus propietarios en $1000’s.

La función nunique() se utiliza para calcular el número de valores únicos en cada columna de un DataFrame de Pandas. Si aplicas esta función a un DataFrame llamado data, proporcionará la cantidad de valores únicos en cada columna de ese DataFrame.

In [ ]:
data.nunique()
Out[ ]:
crim       504
zn          26
indus       76
chas         2
nox         81
rm         446
age        356
dis        412
rad          9
tax         66
ptratio     46
black      357
lstat      455
medv       229
dtype: int64
In [ ]:
data.isnull().sum()
Out[ ]:
crim       0
zn         0
indus      0
chas       0
nox        0
rm         0
age        0
dis        0
rad        0
tax        0
ptratio    0
black      0
lstat      0
medv       0
dtype: int64
In [ ]:
(data.isnull().sum()/(len(data)))*100
Out[ ]:
crim      0.0000
zn        0.0000
indus     0.0000
chas      0.0000
nox       0.0000
rm        0.0000
age       0.0000
dis       0.0000
rad       0.0000
tax       0.0000
ptratio   0.0000
black     0.0000
lstat     0.0000
medv      0.0000
dtype: float64
In [ ]:
data['chas'] = data['chas'].astype(object)

los estadísticos descriptivos generados por describe() se centrarán en las columnas numéricas, y la variable de tipo "objeto" no se incluirá en la salida.

In [ ]:
data.describe().T
Out[ ]:
count mean std min 25% 50% 75% max
crim 506.0000 3.6135 8.6015 0.0063 0.0820 0.2565 3.6771 88.9762
zn 506.0000 11.3636 23.3225 0.0000 0.0000 0.0000 12.5000 100.0000
indus 506.0000 11.1368 6.8604 0.4600 5.1900 9.6900 18.1000 27.7400
nox 506.0000 0.5547 0.1159 0.3850 0.4490 0.5380 0.6240 0.8710
rm 506.0000 6.2846 0.7026 3.5610 5.8855 6.2085 6.6235 8.7800
age 506.0000 68.5749 28.1489 2.9000 45.0250 77.5000 94.0750 100.0000
dis 506.0000 3.7950 2.1057 1.1296 2.1002 3.2074 5.1884 12.1265
rad 506.0000 9.5494 8.7073 1.0000 4.0000 5.0000 24.0000 24.0000
tax 506.0000 408.2372 168.5371 187.0000 279.0000 330.0000 666.0000 711.0000
ptratio 506.0000 18.4555 2.1649 12.6000 17.4000 19.0500 20.2000 22.0000
black 506.0000 356.6740 91.2949 0.3200 375.3775 391.4400 396.2250 396.9000
lstat 506.0000 12.6531 7.1411 1.7300 6.9500 11.3600 16.9550 37.9700
medv 506.0000 22.5328 9.1971 5.0000 17.0250 21.2000 25.0000 50.0000

num_cols = data.select_dtypes(include=np.number).columns.tolist(): En esta línea, se utiliza el método select_dtypes nuevamente para seleccionar todas las columnas del DataFrame data que tienen un tipo de dato numérico. Esto se hace utilizando np.number como argumento para include. Luego, se utiliza columns para obtener los nombres de estas columnas numéricas y tolist() se usa comúnmente cuando necesitas convertir un objeto iterable, como una Serie de Pandas, en una lista de Python.

In [ ]:
cat_cols=data.select_dtypes(include=['object']).columns
num_cols = data.select_dtypes(include=np.number).columns.tolist()
print("Categorical Variables:")
print(cat_cols)
print("Numerical Variables:")
print(num_cols)
Categorical Variables:
Index(['chas'], dtype='object')
Numerical Variables:
['crim', 'zn', 'indus', 'nox', 'rm', 'age', 'dis', 'rad', 'tax', 'ptratio', 'black', 'lstat', 'medv']

Variables numéricas:¶

plt.figure(figsize=(14, 6)): Se crea una figura de Matplotlib para los gráficos. Los gráficos se mostrarán uno al lado del otro en la misma figura.

plt.subplot(1, 2, 1): Se crea el primer subgráfico en una cuadrícula de 1 fila y 2 columnas. Este será el histograma de la columna numérica.

data[col].hist(grid=False): Se crea un histograma de la columna numérica y se muestra en el primer subgráfico.

plt.subplot(1, 2, 2): Se crea el segundo subgráfico en la misma cuadrícula. Este será un gráfico de caja (boxplot) de la columna numérica.

sns.boxplot(x=data[col]): Se crea un gráfico de caja de la columna numérica y se muestra en el segundo subgráfico.

plt.show(): Se muestra la figura con los dos subgráficos.

En resumen, este código realiza un análisis exploratorio de las columnas numéricas en tu DataFrame. Para cada columna, muestra el sesgo, la curtosis y crea un histograma y un gráfico de caja para visualizar la distribución de los datos. Esto puede ser útil para comprender la forma y la dispersión de tus datos numéricos.

In [ ]:
custom_palette = sns.color_palette("Set2")
sns.set(font_scale=1.4)
for col in num_cols:
    print('Column: ', col)
    print('Skew:', round(data[col].skew(), 2))
    print('Kurtosis: ', round(data[col].kurtosis(), 2))
    plt.figure(figsize = (9, 3))
    plt.subplot(1, 2, 1)
    data[col].hist(grid=False, color=custom_palette[2]) #sin fondo cuadriculado y primer color de los colores personalizados
    plt.subplot(1, 2, 2)
    sns.boxplot(x=data[col], palette='Set2')
    plt.show()
Column:  crim
Skew: 5.22
Kurtosis:  37.13
No description has been provided for this image
Column:  zn
Skew: 2.23
Kurtosis:  4.03
No description has been provided for this image
Column:  indus
Skew: 0.3
Kurtosis:  -1.23
No description has been provided for this image
Column:  nox
Skew: 0.73
Kurtosis:  -0.06
No description has been provided for this image
Column:  rm
Skew: 0.4
Kurtosis:  1.89
No description has been provided for this image
Column:  age
Skew: -0.6
Kurtosis:  -0.97
No description has been provided for this image
Column:  dis
Skew: 1.01
Kurtosis:  0.49
No description has been provided for this image
Column:  rad
Skew: 1.0
Kurtosis:  -0.87
No description has been provided for this image
Column:  tax
Skew: 0.67
Kurtosis:  -1.14
No description has been provided for this image
Column:  ptratio
Skew: -0.8
Kurtosis:  -0.29
No description has been provided for this image
Column:  black
Skew: -2.89
Kurtosis:  7.23
No description has been provided for this image
Column:  lstat
Skew: 0.91
Kurtosis:  0.49
No description has been provided for this image
Column:  medv
Skew: 1.11
Kurtosis:  1.5
No description has been provided for this image

Variables categóricas: Usando diagramas de barras representamos la variable dummy del Río Charles (= 1 si el tramo limita con el río; 0 en caso contrario)

In [ ]:
matplotlib.rc_file_defaults()

data['chas'].value_counts(): Esta parte cuenta la frecuencia de cada valor único en la columna 'chas' de tu DataFrame 'data'. Esto significa que cuenta cuántas veces aparece cada categoría única en la columna 'chas'.

.index: Después de contar las frecuencias, value_counts() devuelve una Serie de Pandas que tiene como índice las categorías únicas (valores únicos) de la columna 'chas' y como valores las frecuencias de esas categorías.

order=data['chas'].value_counts().index: Finalmente, esta parte se utiliza para especificar el orden en el que se deben mostrar las categorías en el eje x del gráfico countplot. Se utiliza el índice de la Serie devuelta por value_counts() para determinar el orden. Esto significa que las categorías se mostrarán en el orden en el que aparecen en el índice, que generalmente es en función de su frecuencia, comenzando por la categoría más frecuente.

In [ ]:
sns.countplot(x = 'chas', data = data, color =custom_palette[4] , order = data['chas'].value_counts().index);
No description has been provided for this image

Transformación de datos: Las variables por ejemplo crime y black, por ejemplo, están muy sesgadas y en una escala mayor. Hagamos una transformación logarítmica. La transformación logarítmica puede ayudar en la normalización, por lo que esta variable puede mantener la escala estándar con otras variables

In [ ]:
def log_transform(data,col):
    for colname in col:
        if (data[colname] == 1.0).all():
            data[colname + '_log'] = np.log(data[colname]+1)
        else:
            data[colname + '_log'] = np.log(data[colname])
    data.info()
In [ ]:
log_transform(data,['crim','black'])
<class 'pandas.core.frame.DataFrame'>
Index: 506 entries, 1 to 506
Data columns (total 16 columns):
 #   Column     Non-Null Count  Dtype  
---  ------     --------------  -----  
 0   crim       506 non-null    float64
 1   zn         506 non-null    float64
 2   indus      506 non-null    float64
 3   chas       506 non-null    object 
 4   nox        506 non-null    float64
 5   rm         506 non-null    float64
 6   age        506 non-null    float64
 7   dis        506 non-null    float64
 8   rad        506 non-null    int64  
 9   tax        506 non-null    int64  
 10  ptratio    506 non-null    float64
 11  black      506 non-null    float64
 12  lstat      506 non-null    float64
 13  medv       506 non-null    float64
 14  crim_log   506 non-null    float64
 15  black_log  506 non-null    float64
dtypes: float64(13), int64(2), object(1)
memory usage: 67.2+ KB
In [ ]:
sns.distplot(data["crim_log"], axlabel="crim_log");
No description has been provided for this image
In [ ]:
sns.distplot(data["black_log"], axlabel="black_log");
No description has been provided for this image

Análisis bivariado: Pasemos ahora al análisis bivariado. El análisis bivariado ayuda a comprender cómo se relacionan las variables entre sí y la relación entre las variables dependientes e independientes presentes en el conjunto de datos. Puede utilizar el siguiente comando para visualizar todos los scatter plots, para las posibles relaciones

In [ ]:
sns.pairplot(data=data.drop(['chas', 'black_log', 'crim_log'],axis=1));
No description has been provided for this image
In [ ]:
def scatter_regplot(data, strx, stry):
    sns.set(font_scale=1.4)
    fig, ax = plt.subplots(1, 2, figsize=(14, 6), sharey=True)
    sns.scatterplot(data=data, x=strx, y=stry, ax=ax[0])
    sns.regplot(data=data, x=strx, y=stry, ax=ax[1]);
    fig.suptitle('Relación entre %s y medv'%col)
In [ ]:
num_cols.remove('medv')
for col in num_cols:
    scatter_regplot(data, col, 'medv')
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image
No description has been provided for this image

Un mapa de calor se utiliza ampliamente para este tipo de análisis. El mapa de calor muestra la correlación entre las variables, ya sea positiva o negativa

annot=True: Esto indica que se deben mostrar los valores de correlación en el mapa de calor.

vmin=-1 y vmax=1: Estos argumentos establecen los valores mínimo y máximo en la escala de colores para el mapa de calor, lo que garantiza que los valores de correlación estén en el rango de -1 a 1.

In [ ]:
matplotlib.rc_file_defaults()
sns.set(font_scale=1.2)
plt.figure(figsize=(12, 7))
sns.heatmap(data.drop(['chas', 'black_log', 'crim_log'],axis=1).corr(), annot = True, vmin = -1, vmax = 1);
No description has been provided for this image

EJEMPLO:¶

A manera de ejemplo, utilizaremos el dataset mglearn.datasets.load_extended_boston() de las 104 características resultantes de las 13 características originales junto con las 91 combinaciones posibles de dos características dentro de esas 13

In [ ]:
X, y = mglearn.datasets.load_extended_boston()
In [ ]:
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=0)
lr = LinearRegression().fit(X_train, y_train)

Al comparar los score de los conjuntos de entrenamiento y de prueba, comprobamos que predecimos con mucha precisión en el conjunto de entrenamiento, pero el R^2 en el conjunto de prueba es mucho peor

In [ ]:
print("Training set score: {:.2f}".format(lr.score(X_train, y_train)))
print("Test set score: {:.2f}".format(lr.score(X_test, y_test)))
Training set score: 0.95
Test set score: 0.61

Esta discrepancia entre el rendimiento en el conjunto de entrenamiento y el conjunto de prueba es un claro signo de overfitting, y por lo tanto, debemos tratar de encontrar un modelo que nos permita controlar la complejidad. Usualmente, en este tipo de casos utilizamos técnicas de regularización. Una de las alternativas más utilizadas a la regresión lineal estándar es la regresión ridge, que estudiaremos a continuación.

REGRESIÓN RIDGE¶

La regresión ridge se implementa en linear_model.Ridge. Veamos qué tal lo hace en el conjunto de datos ampliado de Boston Housing.

In [ ]:
ridge = Ridge().fit(X_train, y_train)
print("Training set score: {:.2f}".format(ridge.score(X_train, y_train)))
print("Test set score: {:.2f}".format(ridge.score(X_test, y_test)))
Training set score: 0.89
Test set score: 0.75

Como puede ver, el score en el conjunto de entrenamiento de Ridge es menor que el de la regresión lineal, cuyos puntajes fueron: Training set score: 0.95 and Test set score: 0.61. Además, la puntuación en el conjunto de prueba es mayor. En este caso, Ridge, usa alpha=1.0 como parámetro por default

Ridge es un modelo más restringido, por lo que existe menos probabilidad de overfitting. Un modelo menos complejo significa un peor rendimiento en el conjunto de de entrenamiento, pero uno mejor generalización. Como sólo nos interesa el rendimiento de la generalización, deberíamos elegir el modelo Ridge en lugar del modelo de regresión lineal.

El modelo Ridge hace un balance entre la simplicidad del modelo (coeficientes casi nulos) y su rendimiento en el conjunto de entrenamiento. La importancia que el modelo da a la simplicidad frente al rendimiento del conjunto de entrenamiento, puede ser especificada por el usuario, utilizando el parámetro alpha

Aumentar alpha obliga a los coeficientes a acercarse más a cero, lo que disminuye el rendimiento del conjunto de entrenamiento, pero puede ayudar a la generalización

In [ ]:
ridge10 = Ridge(alpha=10).fit(X_train, y_train)
print("Training set score: {:.2f}".format(ridge10.score(X_train, y_train)))
print("Test set score: {:.2f}".format(ridge10.score(X_test, y_test)))
Training set score: 0.79
Test set score: 0.64

La disminución de alpha permite que los coeficientes estén menos restringidos. Para valores muy pequeños de alpha, los coeficientes apenas están restringidos, y terminamos con un modelo que se parece a LinearRegression

In [ ]:
ridge01 = Ridge(alpha=0.1).fit(X_train, y_train)
print("Training set score: {:.2f}".format(ridge01.score(X_train, y_train)))
print("Test set score: {:.2f}".format(ridge01.score(X_test, y_test)))
Training set score: 0.93
Test set score: 0.77

Como era de esperarse, la puntuación de entrenamiento es mayor que la de prueba para todos los tamaños de conjuntos de datos, tanto para la regresión lineal como para la ridge. Debido a que la regresión ridge está regularizada, la puntuación de entrenamiento es inferior a la de la regresión lineal en todos los casos. Sin embargo, la puntuación de la prueba de la regresión ridge es mejor, en particular, para los subconjuntos pequeños de datos. Para menos de 400 puntos de datos, la regresión lineal no es capaz de aprender nada. A medida que el modelo dispone de más datos, ambos modelos mejoran, y la regresión lineal alcanza a la ridge.

In [ ]:
mglearn.plots.plot_ridge_n_samples()
No description has been provided for this image

LASSO¶

Una alternativa a la regresión ridge para regularizar la regresión lineal es la regresión lasso. Al igual que con la regresión ridge, el uso de lasso también restringe los coeficientes para que sean cercanos a cero, pero de una forma ligeramente diferente, llamada regularización L1.

Apliquemos lasso al conjunto de datos ampliado de Boston Housing

In [ ]:
lasso = Lasso().fit(X_train, y_train)
print("Training set score: {:.2f}".format(lasso.score(X_train, y_train)))
print("Test set score: {:.2f}".format(lasso.score(X_test, y_test)))
print("Number of features used: {}".format(np.sum(lasso.coef_ != 0)))
Training set score: 0.29
Test set score: 0.21
Number of features used: 4

Como se puede ver, Lasso lo hace bastante mal, tanto en el conjunto de entrenamiento como en el de prueba. Esto indica underfitting, pero, nótese que sólo utilizó 4 de las 105 características (feature selection). De forma similar a Ridge, Lasso también tiene un parámetro de regularización, alpha, que controla la fuerza con la que los coeficientes son empujados hacia cero. En el ejemplo anterior, utilizamos el valor por defecto de alpha=1.0. Para reducir underfitting, intentemos disminuir alpha. Cuando hacemos esto, también necesitamos aumentar el ajuste por defecto de max_iter (número máximo de iteraciones a ejecutar)

In [ ]:
lasso001 = Lasso(alpha=0.01, max_iter=100000).fit(X_train, y_train)
print("Training set score: {:.2f}".format(lasso001.score(X_train, y_train)))
print("Test set score: {:.2f}".format(lasso001.score(X_test, y_test)))
print("Number of features used: {}".format(np.sum(lasso001.coef_ != 0)))
Training set score: 0.90
Test set score: 0.77
Number of features used: 33

Un alpha más bajo nos permitió ajustar un modelo más complejo, que funcionó mejor en los datos de entrenamiento y de prueba. El rendimiento es ligeramente mejor que utilizando Ridge, y estamos utilizando sólo 33 de las 105 características. Esto hace que este modelo sea potencialmente más fácil de entender. Sin embargo, si fijamos alpha demasiado bajo, volvemos a eliminar el efecto de la regularización y acabamos en overfitting, con un resultado similar al de LinearRegression

In [ ]:
lasso00001 = Lasso(alpha=0.0001, max_iter=100000).fit(X_train, y_train)
print("Training set score: {:.2f}".format(lasso00001.score(X_train, y_train)))
print("Test set score: {:.2f}".format(lasso00001.score(X_test, y_test)))
print("Number of features used: {}".format(np.sum(lasso00001.coef_ != 0)))
Training set score: 0.95
Test set score: 0.64
Number of features used: 96

MODELOS LINEALES PARA CLASIFICACIÓN¶

Podemos aplicar los modelos LogisticRegression y LinearSVC al conjunto de datos forge y visualizar la forntera de decisión encontrado por los modelos lineales.A pesar de su nombre, LogisticRegression es un algoritmo de clasificación y no de regresión, por lo tanto no debe confundirse con LinearRegression

In [ ]:
sns.set_style("darkgrid") #Esta línea de código establece el estilo de los gráficos creados con
#Seaborn en "darkgrid", lo que significa un estilo con un fondo oscuro y líneas de cuadrícula.
In [ ]:
X, y = mglearn.datasets.make_forge()

Este código en Python utiliza las bibliotecas matplotlib, mglearn, y algún modelo de machine learning (como LinearSVC y LogisticRegression) para visualizar los límites de decisión y los datos en un conjunto de datos bidimensional. A continuación, se explica paso a paso lo que hace:

fig, axes = plt.subplots(1, 2, figsize=(15, 5)): Se crea una figura (fig) con dos subplots (axes) dispuestos en una fila (1) y dos columnas (2) y se especifica un tamaño de figura de 15 unidades de ancho y 5 unidades de alto.

Un bucle for itera sobre una lista de modelos (LinearSVC() y LogisticRegression()) junto con los ejes correspondientes (ax) en los que se trazarán los gráficos.

Dentro del bucle for, se ajusta cada modelo (clf = model.fit(X, y)) al conjunto de datos X e y. Esto implica entrenar los modelos de clasificación en los datos bidimensionales X con etiquetas y.

Se utiliza mglearn.plots.plot_2d_separator() para trazar el límite de decisión del modelo en el espacio de características bidimensional (X). Los parámetros fill=False, eps=0.5 y alpha=.7 controlan el estilo de visualización del límite de decisión.

mglearn.discrete_scatter() se utiliza para trazar los puntos de datos (X[:, 0] y X[:, 1]) en el gráfico y colorearlos de acuerdo con las etiquetas de clase y.

ax.set_title(), ax.set_xlabel() y ax.set_ylabel() se utilizan para establecer el título y las etiquetas de los ejes para cada gráfico. El título se establece en función del nombre de la clase del modelo (clf.class.name).

axes[0].legend() agrega una leyenda al primer gráfico para indicar la relación entre los puntos de datos y las clases. Esto se hace solo en el primer gráfico (axes[0]).

In [ ]:
fig, axes = plt.subplots(1, 2, figsize=(15, 5))

for model, ax in zip([LinearSVC(), LogisticRegression()], axes):
    clf = model.fit(X, y)
    mglearn.plots.plot_2d_separator(clf, X, fill=False, eps=0.5, ax=ax, alpha=.7)
    mglearn.discrete_scatter(X[:, 0], X[:, 1], y, ax=ax)
    ax.set_title("{}".format(clf.__class__.__name__))
    ax.set_xlabel("Feature 0")
    ax.set_ylabel("Feature 1")
    axes[0].legend()
    axes[1].legend()
No artists with labels found to put in legend.  Note that artists whose label start with an underscore are ignored when legend() is called with no argument.
No description has been provided for this image

Para LogisticRegression y LinearSVC el parámetro de compensación que determina la fuerza de la regularización se llama C, y los valores más altos de C corresponden a menor regularización

En otras palabras, cuando se utiliza un valor alto para el parámetro C, LogisticRegression y LinearSVC intentan ajustarse al conjunto de entrenamiento lo mejor posible, mientras que con valores bajos del parámetro C, los modelos ponen más énfasis en encontrar un vector de coeficientes $\beta$ que se acerque a cero. Por defecto, C=1.

Modelos lineales para la clasificación multiclase¶

Muchos modelos de clasificación lineal sólo sirven para la clasificación binaria y no se extienden de forma natural al caso multiclase (con la excepción de la regresión logística). Una técnica común para extender un algoritmo de clasificación binaria a un algoritmo de clasificación multiclase es el enfoque one-vs.-rest. En el enfoque one-vs.-rest, se aprende un modelo binario para cada clase fija, el cual intenta separar esa clase de todas las demás, lo cual da lugar a tantos modelos binarios como clases exista. Para hacer una predicción, se ejecutan todos los clasificadores binarios en un punto de prueba

In [ ]:
from sklearn.datasets import make_blobs
X, y = make_blobs(random_state=42)
plt.figure(figsize=(8, 8))
mglearn.discrete_scatter(X[:, 0], X[:, 1], y)
plt.xlabel("Feature 0")
plt.ylabel("Feature 1")
plt.legend(["Class 0", "Class 1", "Class 2"]);
No description has been provided for this image

Ahora, entrenamos un clasificador LinearSVC en el conjunto de datos

In [ ]:
linear_svm = LinearSVC().fit(X, y)
print("Coefficient shape: ", linear_svm.coef_.shape)
print("Intercept shape: ", linear_svm.intercept_.shape)
Coefficient shape:  (3, 2)
Intercept shape:  (3,)

Vemos que la dimensión (shape) de coef_ es (3, 2), lo que significa que cada fila de coef_ contiene el vector de coeficientes para cada una de las tres clases y cada columna contiene el valor del coeficiente para cada característica específica (hay dos en este conjunto de datos). La matriz intercept_ es ahora una matriz unidimensional que almacena los interceptos de cada clase

Visualicemos las líneas dadas por los tres clasificadores binarios. En este caso line=x, para el clasificador separador ax+by+c=0.

máquina de vectores de soporte lineal:

In [ ]:
mglearn.discrete_scatter(X[:, 0], X[:, 1], y)
line = np.linspace(-15, 15)

for coef, intercept, color in zip(linear_svm.coef_, linear_svm.intercept_, ['b', 'r', 'g']):
    plt.plot(line, -(line * coef[0] + intercept) / coef[1], c=color)
    plt.ylim(-10, 15)
    plt.xlim(-10, 8)
    plt.xlabel("Feature 0")
    plt.ylabel("Feature 1")
    plt.legend(['Class 0', 'Class 1', 'Class 2', 'Line class 0', 'Line class 1', 
                'Line class 2'], loc=(1.01, 0.3))
No description has been provided for this image